const path = require('path');
const _ = require('lodash');
const webpack = require('webpack');

const HappyPack = require('happypack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const AssetsPlugin = require('assets-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssoWebpackPlugin = require('csso-webpack-plugin').default;
const CompressionPlugin = require('compression-webpack-plugin');

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');

const CopyWebpackPlugin = require('copy-webpack-plugin');

const utils = require('./webpack.util.js');

// sentry sourcemap upload
// const SentryCliPlugin = require('@sentry/webpack-plugin');

const happyThreadPool = HappyPack.ThreadPool({ size: 5 });

const { serverIP = 'http://intra.beibotop.com', NODE_ENV } = process.env;
const DEV = NODE_ENV !== 'production';

const constList = utils.getEntryMap(utils.getAllFileList('./src/const'));

function getConstMap() {
  const provideMap = {};
  const aliasMap = {};
  constList.forEach((v) => {
    provideMap[v] = [v, 'default'];
    aliasMap[v] = path.resolve(__dirname, 'src', `const/${v}`);
});
  return {
    provideMap,
    aliasMap,
  };
}

const appConfig = {
  entry: {
    index: './src/main.js',
  },
  port: 3004,
  entries: {
    index: {
      title: '后台',
      template: './entry/index.html',
      chunks: ['commons', 'base', 'index'],
    },
  },
  output: {
    path: 'main',
    publicPath: '/',
  },
};

//= =============entry================
const entry = _.reduce(
  appConfig.entry,
  (entries, entry, entryName) => {
    if (DEV) {
      entry = [`webpack-dev-server/client?http://localhost:${appConfig.port}`, 'webpack/hot/only-dev-server'].concat(entry);
    }
    entries[entryName] = entry;

    return entries;
  },
  {},
);

//= =============output================
const output = {
  path: path.resolve(__dirname, `www/${appConfig.output.path}`),
  publicPath: appConfig.output.publicPath,
};

if (DEV) {
  // output.publicPath = 'http://localhost:' + appConfig.port + appConfig.output.publicPath;
  output.filename = '[name].bundle.js';
  output.chunkFilename = '[name].bundle.js';
} else {
  output.filename = '[name].[hash].bundle.js';
  output.chunkFilename = '[name].[hash].bundle.js';
  // output.sourceMapFilename = '[name].[hash].js.map';
}

//= =============plugins================
const plugins = [
  new webpack.ProvidePlugin({
    Vue: ['vue/dist/vue.esm.js', 'default'],
    mapState: ['vuex', 'mapState'],
    mapGetters: ['vuex', 'mapGetters'],
    _: 'lodash',
    moment: 'moment',
    Velocity: 'velocity-animate',
    ...getConstMap().provideMap,
    code: ['code', 'default'],
    types: ['types', 'default'],
  }),
  new webpack.ContextReplacementPlugin(/moment[\\\/]locale$/, /zh-cn/),

  new HappyPack({
    id: 'js',
    threadPool: happyThreadPool,
    loaders: ['babel-loader'],
  }),
  new HappyPack({
    id: 'scss',
    threadPool: happyThreadPool,
    loaders: [
      'style-loader',
      'css-loader',
      'postcss-loader',
      'sass-loader',
      // {
      //   loader: 'sass-resources-loader',
      //   options: {
      //     resources: './src/base/styles/_variable.scss',
      //   },
      // },
    ],
  }),
  new webpack.optimize.SplitChunksPlugin({
    chunks: 'all',
    minSize: 30000,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    name: true,
    cacheGroups: {
      default: false,
      // default: {
      //   minChunks: 2,
      //   priority: -20,
      //   reuseExistingChunk: true,
      // },
      commons: {
        name: 'commons',
        chunks: 'initial',
        minChunks: 2,
        test: /[\\/]node_modules[\\/]/,
        priority: -5,
        reuseExistingChunk: true,
      },
    },
  }),
  new webpack.DefinePlugin({
    'process.env': {
      ENVIRONMENT: JSON.stringify(process.env.ENVIRONMENT),
      NODE_ENV: JSON.stringify(process.env.NODE_ENV),
      CONST_LIST: JSON.stringify(constList),
    },
  }),
];

if (process.env.MODE === 'analyse') {
  plugins.push(new BundleAnalyzerPlugin());
}

if (process.env.BABEL_ENV !== 'test') {
  plugins.push(
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require(`./src/dll/${process.env.NODE_ENV}/vendor-manifest.json`),
      extensions: ['', '.js'],
    }),
  );
}

if (DEV) {
  if (process.env.BABEL_ENV !== 'test') {
    plugins.push(new webpack.HotModuleReplacementPlugin());
  }
} else {
  // plugins.push(new ExtractTextPlugin('[name].[hash].styles.css'));
  plugins.push(
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: '[name].[hash].styles.css',
      chunkFilename: '[id].[hash].styles.css',
    }),
  );
  plugins.push(new CssoWebpackPlugin());

  plugins.push(new AssetsPlugin());
  plugins.push(
    new UglifyJsPlugin({
      sourceMap: true,
      // uglifyOptions: {
      //   compress: {
      //     inline: false,
      //     // warnings: false
      //   }
      // },
    }),
  );
  plugins.push(new CompressionPlugin());

  // plugins.push(new SentryCliPlugin({
  //   release: PROJECT_VERSION,
  //   include: './www/main',
  //   ignoreFile: '.sentrycliignore',
  //   ignore: ['node_modules', 'webpack.config.js'],
  // }))
}

_.each(appConfig.entries, (entryInfo, entryName) => {
  plugins.push(
    new HtmlWebpackPlugin({
      title: entryInfo.title,
      filename: `${entryName}.html`,
      template: entryInfo.template,
      chunks: entryInfo.chunks,
      chunksSortMode: 'manual',
      inject: 'body',
      favicon: entryInfo.favicon,
      resources: entryInfo.resources,
    }),
  );
});

plugins.push(
  new AddAssetHtmlPlugin([
    {
      filepath: path.resolve(`./src/dll/${process.env.NODE_ENV}/*.styles.css`),
      typeOfAsset: 'css',
      hash: true,
      includeSourcemap: false,
    },
    {
      filepath: path.resolve(`./src/dll/${process.env.NODE_ENV}/*.js`),
      hash: true,
      includeSourcemap: true,
    },
  ]),
);

// copy custom static assets
plugins.push(
  new CopyWebpackPlugin([
    {
      from: path.resolve(__dirname, './src/dll/production'),
      to: '',
      ignore: ['*.js','*.map','*.css','*.json'],
    },
  ]),
  new CopyWebpackPlugin([
    {
      from: path.resolve(__dirname, './static'),
      to: '',
      ignore: ['.*'],
    },
  ]),
);

//= =============modules================
const modules = {
  noParse: appConfig.noParse,
  rules: [
    {
      test: /\.(jpg|gif)$/,
      use: ['url-loader?limit=1024'],
    },
    {
      test: /\.png$/,
      use: ['url-loader?limit=1024!mimetype=image/png!./file.png'],
    },
    {
      test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
      use: 'url-loader?limit=10000&minetype=application/font-woff',
    },
    {
      test: /\.svg$/,
      loader: 'svg-sprite-loader',
      include: [path.resolve(__dirname, 'src/icons')],
      options: {
        symbolId: 'icon-[name]',
      },
    },
    {
      test: /\.(ttf|eot|svg|swf|mp3|wav)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
      use: 'file-loader',
      exclude: [path.resolve(__dirname, 'src/icons')],
    },
    {
      test: /(.*)\.html$/,
      use: ['html-loader'],
      include: [path.resolve(__dirname, 'src/apps')],
    },
    // {
    //   test: /snap/,
    //   use: 'imports-loader?this=>window,fix=>module.exports=0'
    // },
    {
      test: /\.vue$/,
      type: 'javascript/auto',
      use: [
        {
          loader: 'vue-loader',
          options: {
            loaders: {
              js: 'happypack/loader?id=js',
              // js: 'babel-loader',
              // scss: 'happypack/loader?id=scss',
              scss: DEV
                ? [
                  'style-loader',
                  'css-loader',
                  'postcss-loader',
                  'sass-loader',
                  // {
                  //   loader: 'sass-resources-loader',
                  //   options: {
                  //     resources: './src/base/styles/_variable.scss',
                  //   },
                  // },
                ]
                : [
                  'style-loader',
                  MiniCssExtractPlugin.loader,
                  'css-loader',
                  'postcss-loader',
                  'sass-loader',
                  // {
                  //   loader: 'sass-resources-loader',
                  //   options: {
                  //     resources: './src/base/styles/_variable.scss',
                  //   },
                  // },
                ],
            },
          },
        },
      ],
      include: [path.resolve(__dirname, 'src'), path.resolve(__dirname, 'test')],
    },
    {
      test: /\.js$/,
      type: 'javascript/auto',
      // use: ['babel-loader'],
      use: 'happypack/loader?id=js',
      include: [path.resolve(__dirname, 'src'), path.resolve(__dirname, 'test')],
    },
  ],
};

if (DEV) {
  modules.rules.push({
    test: /\.scss$/,
    use: 'happypack/loader?id=scss',
    // use: [
    //   'style-loader',
    //   'css-loader',
    //   'postcss-loader',
    //   'sass-loader',
    //   {
    //     loader: 'sass-resources-loader',
    //     options: {
    //       resources: './src/base/styles/_variable.scss',
    //     },
    //   },
    // ],
    include: [path.resolve(__dirname, 'src')],
  });

  modules.rules.push({
    test: /\.css$/,
    use: ['style-loader', 'css-loader', 'postcss-loader'],
  });

  // modules.rules.push({
  //   enforce: 'pre',
  //   test: /\.(js|vue)$/,
  //   exclude: /node_modules/,
  //   use: [
  //     {
  //       loader: 'eslint-loader',
  //       options: {
  //         configFile: './.eslintrc',
  //       },
  //     },
  //   ],
  // });
} else {
  modules.rules.push({
    test: /\.scss$/,
    use: [
      'style-loader',
      MiniCssExtractPlugin.loader,
      'css-loader',
      'postcss-loader',
      'sass-loader',
      // {
      //   loader: 'sass-resources-loader',
      //   options: {
      //     resources: './src/base/styles/_variable.scss',
      //   },
      // },
    ],
    include: [path.resolve(__dirname, 'src')],
  });

  modules.rules.push({
    test: /\.css$/,
    use: ['style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
  });
}

module.exports = {
  mode: DEV ? 'development' : 'production',
  devtool: DEV ? 'eval-source-map' : 'source-map',
  entry,
  output,
  externals: {
    $: 'jQuery',
  },
  resolve: {
    modules: [path.resolve(__dirname, 'src'), path.resolve(__dirname, 'node_modules'), 'node_modules'],
    extensions: ['.js', '.vue', '.scss', '.html'],
    alias: {
      '@': path.resolve(__dirname, 'src'),
      vue$: 'vue/dist/vue.esm.js',
      ...getConstMap().aliasMap,
      code: path.resolve(__dirname, 'src', 'const/responseCode'),
      types: path.resolve(__dirname, 'src', 'store/mutation-types'),
    },
  },
  plugins,
  module: modules,
  stats: 'errors-only',
  devServer: {
    port: appConfig.port,
    publicPath: appConfig.output.publicPath,
    hot: true,
    contentBase: './src/dll/development',
    clientLogLevel: 'error',
    historyApiFallback: true,
    inline: true,
    progress: false,
    proxy: {
      '/api/**': {
        target: serverIP,
        changeOrigin: true,
      },
      '/acct/imgcode/code': {
        target: serverIP,
        changeOrigin: true,
      },
      '/info/imgs/': {
        target: serverIP,
        changeOrigin: true,
      },
      '/img/*': {
        target: serverIP,
        changeOrigin: true,
      },
    },
    watchOptions: {
      aggregateTimeout: 300,
      poll: 1000,
    },
    headers: { 'X-Custom-Header': 'yes' },
    stats: {
      assets: false,
      cached: false,
      cachedAssets: false,
      children: false,
      chunks: false,
      chunkModules: false,
      chunkOrigins: false,
      colors: true,
      depth: false,
      entrypoints: true,
      hash: true,
      maxModules: 15,
      modules: false,
      performance: true,
      reasons: false,
      source: false,
      timings: true,
      version: false,
      warnings: true,
    },
    // 取消框架域名检测
    disableHostCheck: true,
  },
};
